

let GFS_MyCanvase = null;		// 全屏唯一的

function extractNumber(str){
	return str.match(/\d+/g)?.map(Number) || [];
}


function extractLeadingNumber(str) {
  // 使用正则表达式匹配可能以负号开头的整数或浮点数
  const match = str.match(/^[-+]?(?:\d+\.?\d*|\.\d+)/);

  // 如果找到匹配项，转换为浮点数并返回（自动处理整数和浮点）
  if (match) {
    return parseFloat(match[0]);
  } else {
    // 未匹配到有效数字，返回 null 或 0，根据需求选择
    return 0;
  }
}

/**
 * @param {Object} ctx
 * @param {Object} startPoint
 * @param {Object} endPoint
 * @param {Object} style	 "solid  1px red; 定义样式
 */
function drawLine(ctx, startPoint, endPoint, style){
	if(style!="none"){
		
		// 先替换多个空格为单个，再移除末尾的 ; 和周围空格，最后 trim
		const normalized = style
		  .replace(/ +/g, ' ')           // 多个空格 → 单个空格
		  .replace(/\s*;\s*$/, '')       // 移除末尾的 ; 及其前后空格
		  .trim();                       // 再次 trim 确保首尾无多余空格
		  
		const result = normalized.split(' ');
		
		let [ls, lw, lc] = result
		
		let width = extractLeadingNumber(lw)
		if(width > 0){
			
			if(ls == "dashed" || ls == "dash"){
				ctx.setLineDash([5, 3])
			}
			
			if(ls == "solid"){
				ctx.setLineDash([])
			}
			
			if(lc==undefined)
				lc = 'black'
			
			ctx.strokeStyle = lc
			ctx.lineWidth = width
			ctx.lineCap = "square"
			
			ctx.beginPath()
			ctx.moveTo(startPoint.x, startPoint.y)
			ctx.lineTo(endPoint.x, endPoint.y)
			ctx.stroke()
		}		
	}
}


/**
 * @param {Object} rect   rect.left, rect.top , rect.width, rect.heiht
 * @param {Number} extend >0 扩大， <0 变小
 */
function get4Point(rect, extend=0){
	
	let p0 = {x:rect.left, y:rect.top}
	let p1 = {x:rect.left+rect.width, y:rect.top}
	let p2 = {x:rect.left+rect.width, y:rect.top + rect.height}
	let p3 = {x:rect.left, y:rect.top+rect.height}
	if(extend!=0){
		p0 = {x:p0.x-extend, y:p0.y-extend}
		p1 = {x:p1.x+extend, y:p1.y-extend}
		p2 = {x:p2.x+extend, y:p2.y+extend}
		p3 = {x:p3.x-extend, y:p3.y+extend}
	}
	return [p0, p1, p2, p3]
}


function rectContain(rect, x,  y){
	if(x > rect.left && x < (rect.left + rect.width)  && y>rect.top && y<(rect.top + rect.height))
		return true;
	return  false
}


var IDGEN = 0;
function genID(){
	IDGEN +=1
	return "g"+IDGEN;
}

let isCaretVisible = true; // 光标是否可见
let caretTimer = null;

caretTimer = setInterval(() => {
	
	isCaretVisible = !isCaretVisible;
	
	G_MyCanvaseHolder.forEach((mycav)=>{
		mycav.invalidate = true
		// console.log("isCarevisit", mycav)
	})

	}, 400);
	

let G_MyCanvaseHolder = []

function ReDraw(){
	// mycanvase.redraw()
	G_MyCanvaseHolder.forEach((mycav)=>{
		mycav.redraw()
	})
	requestAnimationFrame(ReDraw)
}
requestAnimationFrame(ReDraw)


class MyCanvase{
	constructor(id, fullScreen=false) {
		this.canvase = document.getElementById(id)
		this.ctx = this.canvase.getContext("2d")
		if(fullScreen){
			GFS_MyCanvase = this
		}
		
		this.shapeListMap = new Map()
		this.shapeListMap.set(0, [])
		
		this.invalidate = true
		this.activeShape = null				// 单击或双击激活的对象
	
		
	
		//  拖动shape 
		this.isDragging = false
		this.dragStartPoint = null
		this.dragShape = null
		
		this.hoverShape = null
	
		// 添加鼠标监听
		this.canvase.addEventListener("dblclick", (event)=> this.handleDbClick(event))
		this.canvase.addEventListener("click", (event)=> this.handleClick(event))
		this.canvase.addEventListener("mousedown", (event)=> this.handleMousedown(event))
		this.canvase.addEventListener("mouseup", (event)=>this.handleMouseup(event))
		this.canvase.addEventListener("mousemove", (event)=>this.handleMousemove(event))
		this.canvase.addEventListener("keydown", (event)=>this.handleKeydown(event))
		
		
		G_MyCanvaseHolder.push(this)
		
	}
	
	
	// 添加到不同的层
	add(shape, layNum=0){
		let sarr = this.shapeListMap.get(layNum)
		// console.log("sarr", sarr)
		if(sarr){
			sarr.unshift(shape)
		}else{
			let narr = []
			this.shapeListMap.set(layNum, narr)
			narr.unshift(shape)
		}
	}
	
	// 移除
	remove(shape, layNum = null){
		if(layNum==null){
			
			let newMap  = new Map()
			this.shapeListMap.forEach((value, key)=>{
				const nlist = value.filter((item)=> item!=shape)
				newMap.set(key, nlist)
			})
			this.shapeListMap = newMap
			
		}else {
			
			let slist = this.shapeListMap.get(layNum)
			if(slist){
				slist.splice(slist.indexOf(shape), 1)
			}
			
		}

		
	}
	
	removeLay(layNum){
		this.shapeListMap.set(layNum, [])
	}
	
	redraw(){
		if(this.invalidate){
			this.ctx.clearRect(0, 0, this.canvase.width, this.canvase.height)
			this.drawGrid()
			
			
			
			// 按照layNum升序排列
			const sorted = [...this.shapeListMap].sort((a, b)=>a[0]-b[0]).map((a)=>a[1])
			sorted.forEach((slist)=>{
				slist.forEach(shape=>{
					shape.draw(this.ctx)
				})
			})
			
			
			
		}
		this.invalidate = false
	}
	
	drawGrid(){
		
		let width = this.canvase.width
		let height = this.canvase.height
		
		// 画竖线
		let x = 0;
		for(;x<width; x+=30){
			
			let start = {x:x, y:0}
			let end = {x:x, y:height}
			drawLine(this.ctx, start, end, "solid 1px #DDDDDD")
		}
		
		let y = 0;
		for(;y<height; y+=30){
			let start = {x:0, y:y}
			let end = {x:width, y:y}
			drawLine(this.ctx, start, end, "solid 1px #DDDDDD")
		}
	}
		
	// 单击激活
	handleClick(event){
		const rect = this.canvase.getBoundingClientRect();
		const x = event.clientX - rect.left;
		const y = event.clientY - rect.top;
	
		//按照layNum降序排列
		const sorted = [...this.shapeListMap].sort((a, b)=>b[0]-a[0]).map((a)=>a[1]).flat()
		
		for(let shap of sorted){
			if(shap.contain(x,y)){
				
				if(shap!=this.activeShape){
					
					if(this.activeShape){
						this.activeShape.inactive()
					}
					this.activeShape = shap
					shap.active()
					this.invalidate = true
				}
				shap.OnClick(x, y)
				return ;
			}
		}
		
		// 空白地方
		if(this.activeShape){
			this.activeShape.inactive()
			this.activeShape = null
			
		}
		this.canvase.focus()
	}
	
	
	
	// 双击激活并编辑
	handleDbClick(event){
		
		const rect = this.canvase.getBoundingClientRect();
		const x = event.clientX - rect.left;
		const y = event.clientY - rect.top;
		
		
		//按照layNum降序排列
		const sorted = [...this.shapeListMap].sort((a, b)=>b[0]-a[0]).map((a)=>a[1]).flat()
		
		
		for(let shap of sorted){
			if(shap.contain(x,y)){
				if(this.activeShape){
					this.activeShape.inactive()
				}
				this.activeShape = shap
				shap.active()
				shap.editing()			// 进入编辑状态
				return ;
			}
		}
		
		// 空白地方
		if(this.activeShape){
			this.activeShape.inactive()
			this.activeShape = null
		}
		
	}
	
	
	// 按下按钮，开始drag
	handleMousedown(event){
		const rect = this.canvase.getBoundingClientRect();
		const x = event.clientX - rect.left;
		const y = event.clientY - rect.top;
		
		this.dragStartPoint = {x:x, y:y}
		this.isDragging = true
		
		//按照layNum降序排列
		const sorted = [...this.shapeListMap].sort((a, b)=>b[0]-a[0]).map((a)=>a[1]).flat()
		
		for(let s of sorted){
			if(s.contain(x, y)){
				this.dragShape = s
				return ;
			}
		}
		this.dragShape = null
	}
	
	handleMousemove(event){
		const rect = this.canvase.getBoundingClientRect();
		const x = event.clientX - rect.left;
		const y = event.clientY - rect.top;
		
		// 拖动
		if(this.isDragging){
			let dx = x - this.dragStartPoint.x
			let dy = y - this.dragStartPoint.y
			
			// 移动激活的shape
			if(this.dragShape  && this.dragShape.contain(this.dragStartPoint.x, this.dragStartPoint.y)){
				this.dragShape.OnDrag(this.dragStartPoint.x, this.dragStartPoint.y, dx, dy)
			}
			this.dragStartPoint.x = x;
			this.dragStartPoint.y = y;
			return 
		}
		
		// hover在某个节点上
		
		// 按照layNum降序排列
		const sorted = [...this.shapeListMap].sort((a, b)=>b[0]-a[0]).map((a)=>a[1]).flat()
		
		
		for(let s of sorted){
			if(s.contain(x, y)){
				if(this.hoverShape!=s){
					if(this.hoverShape){
						this.hoverShape.inhover()
					}
					s.hover()
					this.hoverShape = s;
					this.invalidate = true;
				}
				return ;
			}
		}
		
		// hover 空
		if(this.hoverShape){
			this.hoverShape.inhover()
			this.hoverShape = null
			this.invalidate = true
		}
		
	}
	
	handleMouseup(event){
		const rect = this.canvase.getBoundingClientRect();
		const x = event.clientX - rect.left;
		const y = event.clientY - rect.top;
		
		
		this.isDragging = false
		this.dragShape = null
		
	}
	
	
	handleKeydown(event){
		console.log("mycanvase.js")
	}
}




class Shape{
	constructor(mycanvas) {
	    this.mycanvase = mycanvas
		this.gid = genID()
	}
	
	id(){
		return this.gid
	}
	
	invalidate(){
		this.mycanvase.invalidate = true
	}
	
	// 移动函数
	move(dx, dy){
		
	}
	
	// 实际的绘图
	draw(ctx){
	}
	
	// 判断点击点, 是否命中, 以及鼠标在上面悬浮或浮动时的判断
	contain(x, y){
		
	}
	
	// 状态类函数, Mycanvase的click事件, mousedown 都可以激活状态, 当点击空白的地方就取消active状态
	active(){
		this.isActive = true
	}

	inactive(){
		this.isActive = false
		this.isEditing = false
	}
	
	// 鼠标悬浮的时候
	hover(){
		this.isHover = true
	}
	
	// 鼠标未悬浮的时候
	inhover(){
		this.isHover = false;
	}
	
	// 编辑模式
	editing(){
		this.isEditing = true
	}
	
	// 响应拖动事件 x,y 是起始点点击点， dx， dy是变化量
	OnDrag(x, y, dx, dy){
		
	}
	
	OnClick(x, y){
		
	}
		
}


// 矩形边框
class RectShape extends Shape {
	constructor(mycanvas, left, top, width=200, height=120) {
		super(mycanvas)
		this.left = left
		this.top = top
		this.width = width;
		this.height = height
	}
	
	contain(x, y){
		if(x > this.left && x < (this.left + this.width)  && y>this.top && y<(this.top + this.height))
			return true;
		return  false
	}
	
	move(dx, dy){
		this.left += dx
		this.top  += dy
		this.invalidate()
	}
	
	OnDrag(x, y, dx, dy){
		this.move(dx, dy)
	}
	
	draw(ctx){
		
		let border = this.border ? this.border : "solid 1px black"
		let borderLeft = this.borderLeft ? this.borderLeft : border
		let borderTop = this.borderTop ? this.borderTop : border
		let borderRight = this.borderRight ? this.borderRight:  border
		let borderBottom = this.borderBottom ? this.borderBottom : border
		
		let [p0, p1, p2, p3] = get4Point(this)
		drawLine(ctx, p3, p0, borderLeft)
		drawLine(ctx,p1, p2, borderRight)
		
		drawLine(ctx, p0, p1, borderTop)
		drawLine(ctx, p2, p3, borderBottom)
		
		if(this.background && this.background!="none"){
			ctx.fillStyle=this.background
			ctx.fillRect(this.left, this.top, this.width, this.height)
		}
		
		if(this.isActive){
			let [p0, p1, p2, p3] = get4Point(this, 4)
			let st = "solid 2px rgba(0,0,255,0.6)"
			drawLine(ctx, p0, p1, st)
			drawLine(ctx,p1, p2, st)
			drawLine(ctx, p2, p3, st)
			drawLine(ctx, p3, p0, st)
		}
		
		if(!this.isActive && this.isHover){
			let [p0, p1, p2, p3] = get4Point(this, 4)
			let st = "solid 2px rgba(0,0,255,0.3)"
			drawLine(ctx, p0, p1, st)
			drawLine(ctx,p1, p2, st)
			drawLine(ctx, p2, p3, st)
			drawLine(ctx, p3, p0, st)
		}
	}
}

// 文本框，可输入文字
class TextShape extends RectShape {
	constructor(mycanvas, left, top, width=200, height=80) {
		super(mycanvas, left, top, width, height)
		this.text = "TextShape"
	}
	
	// 根据文字调整 width 和 height
	adjust(){
		
		let ctx = this.mycanvase.ctx
		// 设置字体
		if(this.font){
			ctx.font = this.font
		}else {
			ctx.font = "16px Arial"
		}
		
		let pad = this.padding ? extractLeadingNumber(this.padding) : 8
		
		
		let padLeft = this.paddingLeft ? extractLeadingNumber(this.paddingLeft) : pad
		let padRight = this.paddingRight ? extractLeadingNumber(this.paddingRight) : pad
		let padTop = this.paddingTop ? extractLeadingNumber(this.paddingTop) : pad
		let padBottom = this.paddingBottom ? extractLeadingNumber(this.paddingBottom) : pad
		
		
		let fontSize = extractNumber(ctx.font)[0]
		let lheight = this.lineHeight ? extractLeadingNumber(this.lineHeight) : fontSize*1.5
		let [twidth, theight, tlnum] = calTextArea(ctx, this.text, lheight)
		
		this.width = twidth + padLeft + padRight
		this.height = theight+ padTop + padBottom
		if(this.width < 50)
				this.width = 50
		if(this.height < 20)
				this.height = 20
				
	}
	
	
	draw(ctx){
		
		// 设置字体
		if(this.font){
			ctx.font = this.font
		}else {
			ctx.font = "16px Arial"
		}
		
		let pad = this.padding ? extractLeadingNumber(this.padding) : 8
		
		let padLeft = this.paddingLeft ? extractLeadingNumber(this.paddingLeft) : pad
		let padRight = this.paddingRight ? extractLeadingNumber(this.paddingRight) : pad
		let padTop = this.paddingTop ? extractLeadingNumber(this.paddingTop) : pad
		let padBottom = this.paddingBottom ? extractLeadingNumber(this.paddingBottom) : pad
		
		let fontSize = extractNumber(ctx.font)[0]
		let lheight = this.lineHeight ? extractLeadingNumber(this.lineHeight) : fontSize*1.5
		let [twidth, theight, tlnum] = calTextArea(ctx, this.text, lheight)
		
		this.width = twidth + padLeft + padRight
		this.height = theight+ padTop + padBottom
		if(this.width < 50)
				this.width = 50
		if(this.height < 20)
				this.height = 20
				
		super.draw(ctx)
		
		
		if(this.textColor){
			ctx.fillStyle = this.textColor
		}
		ctx.textBaseline = "top"
		
		// 文字起始点  this.left+pad, this.top+pad，  lheight
		drawText(ctx, this.text, this.left+padLeft, this.top+padTop, lheight)
		
		this.textRect = {left: this.left+padLeft, top:this.top+padTop, width:twidth, height:theight}
		
		// 正在编辑的时候
		// console.log("TexShape", this.isEditing, this.editingNth)
		if(this.isEditing && this.editingNth !=null){
			if(isCaretVisible){
				let [dx, dy, lnum] = calTextNth2Offset(ctx, this.text, this.editingNth, lheight)
				let cx = this.left + padLeft + dx
				let cy = this.top + padTop + dy
				
				let cy1 = cy - 3
				let cy2 = cy1 + fontSize+3 
				drawLine(ctx, {x:cx, y:cy1}, {x:cx, y:cy2}, "solid 2px #333333")
				
				this.caretPosition  = {x:cx, y:cy2}
			}
		}
	}

	
	OnClick(x, y){
		
		// 如果时在编辑状态，点击移动光标位置
		if(this.isEditing && this.textRect){
			if(rectContain(this.textRect, x, y)){
				let offset = [x-this.textRect.left,  y-this.textRect.top]
				let fontSize = extractNumber(this.mycanvase.ctx.font)[0]
				let lheight = this.lineHeight ? extractLeadingNumber(this.lineHeight) : fontSize*1.5
				let nth = calTextOffset2Nth(this.mycanvase.ctx, this.text, offset, lheight)
				if(nth){
					this.editingNth = nth
					this.invalidate()
				}
			}
		}
		
		// let offset = [x-this.left-20, y-this.top-10]
		// // console.log("offset", offset)
		// let nth = calTextOffset2Nth(this.mycanvase.ctx, this.text, offset, 20)
		// console.log("nth=", nth)
		
		// if(nth>=0){
		// 	let ox = this.left + 20
		// 	let [dx, dy, lnum] =calTextNth2Offset(this.mycanvase.ctx, this.text, nth, 20);
		// 	// console.log("[dx, dy, lnum]", dx, dy, lnum)
		// 	let cx = ox + dx;
		// 	let cy = this.top + 10 +dy
		// 	drawLine(this.mycanvase.ctx, {x:cx, y:cy}, {x:cx, y:cy+20}, "solid 1px red")
		// }
		
	}
	
	editing(){
		super.editing()
		this.editingNth = this.text.length
		showInputBox(this)
	}
	
	
	editingInsert(str){
		if(this.editingNth == this.text.length){
			this.text+=str
			this.editingNth = this.text.length
		}else{
			this.text = this.text.slice(0, this.editingNth) + str + this.text.slice(this.editingNth)
			this.editingNth+=str.length
		}
		this.invalidate()
	}
	
	// 从nth位置，向前删除count个字符
	editingDelete(count){
		let old = this.editingNth
		this.editingNth = this.editingNth - count >=0 ? this.editingNth - count : 0
		this.text = this.text.slice(0, this.editingNth) + this.text.slice(old)
		this.invalidate()
	}
	
	
}

// 计算文字区域大小, 以及有多少行
function calTextArea(ctx,text, lineHeight, font=null){	
	var textsplit = text.split('\n')
	var lineNum = textsplit.length
	let height = lineNum * lineHeight
	
	let width = 0
	for(let s of textsplit){
		var metrics = ctx.measureText(s);
		if(metrics.width > width){
			width = metrics.width
		}
	}
	return [width, height, lineNum]
}

// 计算第n个字符的起始相对位置, 返回dx， dy, lnum 所在
function calTextNth2Offset(ctx, text, nth, lineHeight, font=null){
	
	if(nth>text.length){
		nth = text.length
	}
	var textsplit = text.split('\n')
	let cnt = 0
	let lnum = 0
	for(let s of textsplit){
		if(cnt + s.length < nth){
				cnt += s.length+1
				lnum +=1
		}else{
			var offset = nth - cnt
			var targstr = s.substr(0, offset)
			var metrics = ctx.measureText(targstr);
			var dx = metrics.width
			break;
		}
	}
	return [dx, lnum*lineHeight , lnum]
}

// 计算点击的点，在第几个字符之前， offset是相对文字原点的偏移
function calTextOffset2Nth(ctx, text, offset, lineHeight, font=null){
	let [width, height, lineNum] = calTextArea(ctx, text, lineHeight, font)
	let [dx, dy] = offset
	
	// console.log("[width, height, lineNum]", width, height, lineNum)
	// console.log("text", text)
	// 在文字区域内
	if(dx >-10 && dx<width+30 && dy>0 && dy <height){
		let ln = Math.floor(dy / lineHeight)
		// console.log("ln", ln)
		if(ln < lineNum){
			let textsplit = text.split("\n")
			let targstr = textsplit[ln]
			let len = 0
			for(;len<=targstr.length; len++){
				let s1 = targstr.substr(0, len)
				let metric = ctx.measureText(s1)
				if(metric.width > dx){
						break;
				}
			}
			if(len>targstr.length){
				len = targstr.length
			}

			for(let i=0; i< ln ; i++){
				len += textsplit[i].length + 1
			}
			return len
		}
	}
	return null
}
// 绘制多行文本
function drawText(ctx, text, x, y, lineHeight, font=null){
	var textsplit = text.split("\n")
	for(let i=0; i<textsplit.length; i++){
		let cx =x;
		let cy = y + i*lineHeight
		ctx.fillText(textsplit[i], cx, cy)		
	}
}

var g_isComposing = false

function showInputBox(shape) {
	const input = document.createElement("input");
	input.type = "text";
	input.style.border = "none";           // 去掉边框
	input.style.outline = "none";          // 去掉聚焦时的默认轮廓（蓝色/橙色高亮）
	input.style.boxShadow = "none";        // 去掉可能的阴影（某些浏览器或主题下存在）
	input.style.background = "transparent"; // 可选：透明背景，使其更像 Canvas 文本
	
	input.style.font="16px Arial"
	input.style.textAlign="left"
	input.style.position = "absolute";
	
	if(shape.caretPosition){
		
		const rect = shape.mycanvase.canvase.getBoundingClientRect();
		const x = rect.left + shape.caretPosition.x
		const y = rect.top + shape.caretPosition.y
		
		input.style.left = (x-5) + "px"
		input.style.top  = (y+5) + "px"
		
	}else{
		input.style.left = (shape.left + 20) + "px";
		input.style.top = (shape.top + shape.height) + "px";
	}
	
	input.style.width = 0 + "px";
	input.style.height = 0 +"px"
	
	input.onblur = function() {
		// shape.text = input.value;
		// shape.isEditing = false;
		// shape.invalidate()
		input.remove();
		if(shape.isEditing){
			showInputBox(shape)
		}
	};
	
	/**
	 *  1、监听input框的输入事件,为了让输入法随着标签移动，输入一个后，就blur然后重新showinputbox
	 *  2、下面的compositionstart 解决中文输入时的响应
	 */
	input.addEventListener("input", (event)=>{
		if(g_isComposing){
			return 
		}
		// console.log("input event", event.target.value)
		shape.editingInsert(event.target.value)
		input.value=""
		input.blur()
	})
	
	input.addEventListener('compositionstart',(event)=>{
		g_isComposing = true
	})
	
	input.addEventListener('compositionupdate', (event)=>{
		
	})

	input.addEventListener('compositionend', (event)=>{
		g_isComposing = false
		shape.editingInsert(event.target.value)
		input.value=""
		input.blur()
	})
	
	input.addEventListener("keydown", (e)=>{
		if(e.key === 'Enter'){
			e.preventDefault()
			if(e.shiftKey){
				shape.editingInsert('\n')
			}else{
				shape.isEditing = false
				shape.mycanvase.canvase.focus()
				input.blur()
			}
			return 
		}
		
		if (e.key === 'Backspace') {
		        // 注意：只有当输入框为空时，Backspace 才可能用于“删除上一项”
				// console.log("keyDown Backspace")
		        if (input.value === '') {
		            // console.log('输入框为空，Backspace 可用于删除上一个标签/项目');
		            // handleDeleteLastItem();
					shape.editingDelete(1)
					e.preventDefault()
		        }
		        // 如果输入框有内容，浏览器会自动删除最后一个字符
		}
		
		
		if (e.key === 'ArrowRight'){
			if(input.value === ''  && shape.editingNth < shape.text.length){
				 shape.editingNth += 1
				 e.preventDefault()
			}
		}
		
		if (e.key === 'ArrowLeft'){
			if(input.value === '' && shape.editingNth > 0){
				 shape.editingNth -= 1
				 e.preventDefault()
			}
		}
		
		
		
	})
	document.body.appendChild(input);
	input.focus();
}



// 贝塞尔曲线
class BezierCurveShape extends Shape{
	constructor(mycanvas, startPoint, endPoint, ctrPoint1, ctrPoint2 =null,  style=null){
		super(mycanvas)
		this.startPoint = startPoint
		this.endPoint = endPoint
		this.ctrPoint1 = ctrPoint1
		this.ctrPoint2 = ctrPoint2
		this.style = style ? style : "solid 2px black" 
		
		if(this.ctrPoint1 == null){
			this.ctrPoint1 = {x: this.startPoint.x,  y:this.endPoint.y}
		}
	}
	
	draw(ctx){
		
		const normalized = this.style
		  .replace(/ +/g, ' ')           // 多个空格 → 单个空格
		  .replace(/\s*;\s*$/, '')       // 移除末尾的 ; 及其前后空格
		  .trim();                       // 再次 trim 确保首尾无多余空格
		  
		const result = normalized.split(' ');
		
		let [ls, lw, lc] = result
		
		ctx.beginPath();
		ctx.moveTo(this.startPoint.x, this.startPoint.y);
		
		if(this.ctrPoint2){
			
			ctx.bezierCurveTo(
			  this.ctrPoint1.x, this.ctrPoint1.y,
			  this.ctrPoint2.x, this.ctrPoint2.y,
			  this.endPoint.x, this.endPoint.y
			);
			
		}else{
			
			ctx.bezierCurveTo(
			  this.ctrPoint1.x, this.ctrPoint1.y,
			  this.endPoint.x, this.endPoint.y
			);
			
		}
		
		if(ls == "dashed" || ls == "dash"){
			ctx.setLineDash([6, 3])
		}
		
		if(ls == "solid"){
			ctx.setLineDash([])
		}
		
		ctx.lineCap = "butt"
		ctx.lineWidth = extractLeadingNumber(lw)
		ctx.strokeStyle = lc
		ctx.stroke();
		
		
		if(this.drawCtr){
			
			ctx.fillRect(this.ctrPoint1.x, this.ctrPoint1.y, 10, 10)
			drawLine(ctx, this.startPoint, this.ctrPoint1, "dash 1px red")
			
			if(this.ctrPoint2){
				ctx.fillRect(this.ctrPoint2.x, this.ctrPoint2.y, 10, 10)
				drawLine(ctx, this.endPoint, this.ctrPoint2, "dashed  1px red")
			}
		}
	}

	move(dx, dy){
		this.startPoint.x += dx
		this.startPoint.y += dy
		
		this.endPoint.x +=dx
		this.endPoint.y +=dy
		
		this.ctrPoint1.x +=dx
		this.ctrPoint1.y +=dy
		
		if(this.ctrPoint2){
			this.ctrPoint2.x +=dx
			this.ctrPoint2.y +=dy
		}
	}
}


// 水平三次贝塞尔曲线 
class HoriCurveShape extends BezierCurveShape{
	constructor(mycanvas, startPoint, endPoint, style=null) {
	    super(mycanvas, startPoint, endPoint, null, null, style)
		let lx = startPoint.x < endPoint.x ? startPoint.x : endPoint.x
		let ly = startPoint.x < endPoint.x ? startPoint.y : endPoint.y
		
		let rx = startPoint.x < endPoint.x ? endPoint.x: startPoint.x
		let ry = startPoint.x < endPoint.x ? endPoint.y: startPoint.y
		
		let dist = rx - lx
		
		this.ctrPoint2= {x:0, y:0}
		
		if(startPoint.x < endPoint.x){
			
			this.ctrPoint1.x = lx + 0.5 * dist
			this.ctrPoint1.y = ly
			
			this.ctrPoint2.x = rx - 0.5 * dist
			this.ctrPoint2.y = ry
			
		}else{
			
			this.ctrPoint1.x = rx - 0.5 * dist
			this.ctrPoint1.y = ry
			
			this.ctrPoint2.x = lx + 0.5 * dist
			this.ctrPoint2.y = ly
		}
	}
}


function resizeGFS_MyCanvase(){
	
	if(GFS_MyCanvase){
		const width = window.innerWidth
		const height = window.innerHeight
		GFS_MyCanvase.canvase.width = width-10
		GFS_MyCanvase.canvase.height = height-10
		GFS_MyCanvase.invalidate = true
		GFS_MyCanvase.redraw()
	}
	
}


window.addEventListener("load", resizeGFS_MyCanvase)
window.addEventListener("resize", resizeGFS_MyCanvase)




